﻿#include "LuaObject.h"
#include "LuaTable.h"
#include "LuaObjectImpl.h"

LuaObject luaNil;

LuaObject::LuaObject()
	:m_ptr(NULL)
{
}

LuaObject::LuaObject(LuaState* L)
	:m_ptr(LuaObjectImpl::create(L))
{
	
}

LuaObject::LuaObject(LuaState* L,int idx)
	:m_ptr(LuaObjectImpl::createFromIndex(L, idx))
{
}

LuaObject::LuaObject(LuaObjectImpl* impl)
	:m_ptr(impl)
{
}

LuaObject::LuaObject(const LuaObject& p)
	:m_ptr(p.m_ptr) 
{
	if (m_ptr)
		m_ptr->addRef();
}

LuaObject::~LuaObject(void)
{
	if (m_ptr) 
		m_ptr->release();
}

int LuaObject::getIndex()const
{
	if(m_ptr)
		return m_ptr->getIndex();
	else
		return 0;
}

const char* LuaObject::typeName()const
{
	if (m_ptr)
		return lua_typename(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return "None";
}

int LuaObject::getType()const
{
	if (m_ptr)
		return lua_type(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return LUA_TNONE;
}

bool LuaObject::isNumber()const
{
	return getType()==LUA_TNUMBER;
}
bool LuaObject::isString()const
{
	return getType()==LUA_TSTRING;
}
bool LuaObject::isCFunction()const
{
	return getType()==LUA_TFUNCTION;
}
bool LuaObject::isFunction()const
{
	return getType()==LUA_TFUNCTION;
}
bool LuaObject::isUData()const
{
	return getType()==LUA_TUSERDATA;
}

bool LuaObject::isPtr()const
{
	return getType()==LUA_TLIGHTUSERDATA;
}

bool LuaObject::isTable()const
{
	return getType()==LUA_TTABLE;
}
bool LuaObject::isNil()const
{
	return getType()==LUA_TNIL;
}

bool LuaObject::isValid()const {
	int type = getType();
	return type != LUA_TNIL && type != LUA_TNONE;
}

bool LuaObject::isBool()const
{
	return getType()==LUA_TBOOLEAN;
}
bool LuaObject::isThread()const
{
	return getType()==LUA_TTHREAD;
}
bool LuaObject::isNone()const
{
	return getType()==LUA_TNONE;
}

lua_Number LuaObject::toNumber()const
{
	if(m_ptr)
		return lua_tonumber(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return 0;
}

float LuaObject::toFloat()const
{
	if(m_ptr)
		return (float)lua_tonumber(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return 0;
}

lua_Integer LuaObject::toInt()const
{
	if(m_ptr)
		return lua_tointeger(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return 0;
}

lua_Unsigned LuaObject::toUInt()const
{
	if(m_ptr)
		return (lua_Unsigned)lua_tointeger(m_ptr->getLuaState(), m_ptr->getIndex());
	else
		return 0;
}

int32_t  LuaObject::toInt32()const {
	if (m_ptr)
		return (int32_t)lua_tointeger(m_ptr->getLuaState(), m_ptr->getIndex());
	else
		return 0;
}

uint32_t LuaObject::toUInt32()const {
	if (m_ptr)
		return (uint32_t)lua_tointeger(m_ptr->getLuaState(), m_ptr->getIndex());
	else
		return 0;
}

bool LuaObject::toBool()const
{
	if(m_ptr)
		return lua_toboolean(m_ptr->getLuaState(),m_ptr->getIndex())!=0;
	else
		return false;
}
const char* LuaObject::toString()const
{
	const char* str = "";
	if(m_ptr)
		str = lua_tostring(m_ptr->getLuaState(),m_ptr->getIndex());
	if (!str)
		str = "";
	return str;
}

std::wstring LuaObject::toWString()const {
	size_t len;
	std::wstring utf16;
	const char* utf8 = lua_tolstring(m_ptr->getLuaState(), m_ptr->getIndex(), &len);
	if (utf8 && len > 0) {
		int utf16Len = UTF8To16(NULL, utf8, len);
		utf16.resize(utf16Len);
		UTF8To16((unsigned short*)utf16.data(), utf8, len);
	}
	return utf16;
}

const char* LuaObject::toString(size_t *len)const
{
	const char* str = "";
	if (m_ptr)
		str = lua_tolstring(m_ptr->getLuaState(), m_ptr->getIndex(),len);
	if (!str)
		str = "";
	return str;
}

lua_CFunction LuaObject::toCFunction()const
{
	if(m_ptr)
		return lua_tocfunction(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return NULL;
}

void * LuaObject::toData()const
{
	if(m_ptr)
		return lua_touserdata(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return NULL;
}

lua_State* LuaObject::toThread()const
{
	if(m_ptr)
		return lua_tothread(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return NULL;
}

const void* LuaObject::toPtr()const
{
	if(m_ptr)
		return lua_topointer(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return NULL;
}

size_t LuaObject::toConst(const LuaConstDef* tags, size_t def)const
{
	if (!m_ptr)
		return def;

	lua_State * L = m_ptr->getLuaState();
	int type = getType();
	switch (type)
	{
	case LUA_TNIL:
	case LUA_TNONE:
		return def;
	case LUA_TNUMBER:
		return (size_t)lua_tointeger(L, m_ptr->getIndex());
	case LUA_TSTRING:
		{
			const char *key = lua_tostring(L, m_ptr->getIndex());
			for (const LuaConstDef*  c = tags; c->name; ++c) {
				if (0 == strcmp(c->name, key)) {
					return c->code;
				}
			}
			lua_pushfstring(L, "Unknown constant: `%s`", key);
			lua_error(L);
			return def;
		}
	}
	lua_pushstring(L, "Unsupported constant type: ");
	lua_pushstring(L, lua_typename(L, m_ptr->getIndex()));
	lua_concat(L, 2);
	lua_error(L);
	return def;
}

size_t LuaObject::objLen()const
{
	if(m_ptr)
		return lua_objlen(m_ptr->getLuaState(),m_ptr->getIndex());
	else
		return 0;
}


LuaObject& LuaObject::operator=(const LuaObject& other)
{
	if(m_ptr)
	{
		if(other.m_ptr)
		{
			lua_pushvalue(m_ptr->getLuaState(),other.getIndex());
			lua_replace(m_ptr->getLuaState(),m_ptr->getIndex());		
		}
		else
		{
			lua_pushnil(m_ptr->getLuaState());
			lua_replace(m_ptr->getLuaState(),m_ptr->getIndex());
		}
	}
	else if(other.m_ptr)
	{
		m_ptr=other.m_ptr;
		m_ptr->addRef();
	}
	return *this;
}

bool LuaObject::operator==(lua_Integer n)
{
	return toInt() == n;
}

bool LuaObject::operator==(const char* s)
{
	if(isString() && s)
		return strcmp(toString(),s)==0;
	else
		return false;
}

bool LuaObject::operator==(const LuaObject& other)
{
	if (m_ptr && other.m_ptr)
	{
#if LUA_VERSION_NUM >= 503
		return lua_compare(getLuaState(), getIndex(), other.getIndex(), LUA_OPEQ) != 0;
#else
		return lua_equal(getLuaState(),getIndex(),other.getIndex()) != 0;
#endif
	}else{
		return m_ptr==other.m_ptr;
	}
}

bool LuaObject::operator<(const LuaObject& other) {
	if (m_ptr && other.m_ptr){
#if LUA_VERSION_NUM >= 503
		return lua_compare(getLuaState(), getIndex(), other.getIndex(), LUA_OPLT) != 0;
#else
		return lua_lessthan(getLuaState(), getIndex(), other.getIndex()) != 0;
#endif
	}else{
		return false;
	}
}


lua_State* LuaObject::getLuaState() const
{
	if(m_ptr)
		return m_ptr->getLuaState();
	else
		return NULL;
}

LuaState* LuaObject::getCppLuaState() const
{
	if (m_ptr)
		return m_ptr->getCppLuaState();
	else
		return NULL;
}


bool LuaObject::setMetatable(LuaTable tab)
{
	if(m_ptr && tab.isValid())
	{
		lua_State * L=getLuaState();
		lua_pushvalue(L,tab.getIndex());
		return lua_setmetatable(L,getIndex())==LUA_OK;
	}
	return false;
}

LuaTable LuaObject::getMetatable() const
{
	if(m_ptr){
		lua_State * L=getLuaState();
		if (lua_getmetatable(L, getIndex()))
			return LuaObject(getCppLuaState(), lua_gettop(L));
		else
			return luaNil;
	}else {
		return luaNil;
	}

}

LuaObject LuaObject::getUpValue(int n) const
{
	if (isFunction() && lua_getupvalue(getLuaState(), getIndex(), n)) {
		return LuaObject(getCppLuaState(), -1);
	}
	else {
		return luaNil;
	}
}




